---
title: "Tabs"
description: "Tabs organize content into multiple sections and allow users to navigate between them."
---

import {tabsContent} from "@/content/components/tabs";

# Tabs

Tabs organize content into multiple sections and allow users to navigate between them.

<ComponentLinks component="tabs" reactAriaHook="useTabList" />

---

<CarbonAd/>

## Installation

<PackageManagers
  showGlobalInstallWarning
  commands={{
    cli: "npx heroui-cli@latest add tabs",
    npm: "npm install @heroui/tabs",
    yarn: "yarn add @heroui/tabs",
    pnpm: "pnpm add @heroui/tabs",
    bun: "bun add @heroui/tabs"
  }}
/>

## Import

HeroUI exports 2 tabs-related components:

- **Tabs**: The main component to display a tab list.
- **Tab**: The component to display a tab item. The children of this component will be displayed as the content of the tab.

<ImportTabs
  commands={{
    main: 'import {Tabs, Tab} from "@heroui/react";',
    individual: 'import {Tabs, Tab} from "@heroui/tabs";',
  }}
/>

## Usage

<CodeDemo title="Usage" files={tabsContent.usage} />

### Dynamic

You can render tabs dynamically by using `items` prop.

<CodeDemo title="Dynamic" files={tabsContent.dynamic} />

### Disabled

<CodeDemo title="Disabled" files={tabsContent.disabled} />

### Disabled Item

<CodeDemo title="Disabled Item" files={tabsContent.disabledItem} />

### Sizes

<CodeDemo title="Sizes" files={tabsContent.sizes} />

### Radius

<CodeDemo title="Radius" files={tabsContent.radius} />

### Colors

<CodeDemo title="Colors" files={tabsContent.colors} />

### Variants

<CodeDemo title="Variants" files={tabsContent.variants} />

### With Icons

<CodeDemo title="With Icons" files={tabsContent.icons} />

### Controlled

You can use the `onSelectionChange` and `selectedKey` props to control the selected tab.

<CodeDemo title="Controlled" files={tabsContent.controlled} />

### Placement

You can change the position of the tabs by using the `placement` prop. The default value is `top`.

<CodeDemo title="Placement" files={tabsContent.placement} />

### Vertical

Change the orientation of the tabs it will invalidate the placement prop when the value is `true`.

<CodeDemo title="Vertical" files={tabsContent.vertical} />

### Links

Tabs items can be rendered as links by passing the `href` prop to the `Tab` component. By
default, links perform native browser navigation. However, you'll usually want to synchronize
the selected tab with the current URL from your client side router. You can do this by doing
the following:

1. Set up your router at the root of your app. See [Routing guide](/docs/guide/routing) to learn how to do this.
2. Use the `selectedKey` prop to set the selected tab based on the current URL.

#### Next.js

This example uses [Next.js App router](https://nextjs.org/docs/app/api-reference/functions/use-pathname) to
set up routes for each tab and synchronize the selected tab with the current URL.

```tsx
// app/tabs/page.tsx
"use client";

import {usePathname} from "next/navigation";

function ExampleClientRouterWithTabs() {
  const pathname = usePathname();

  return (
    <Tabs aria-label="Options" selectedKey={pathname}>
      <Tab key="/photos" title="Photos" href="/photos">
        <Card>
          <CardBody>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
            incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
            exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
          </CardBody>
        </Card>
      </Tab>
      <Tab key="/music" title="Music" href="/music">
        <Card>
          <CardBody>
            Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
            commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
            dolore eu fugiat nulla pariatur.
          </CardBody>
        </Card>
      </Tab>
      <Tab key="/videos" title="Videos" href="/videos">
        <Card>
          <CardBody>
            Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
            mollit anim id est laborum.
          </CardBody>
        </Card>
      </Tab>
    </Tabs>
  );
}
```

#### React Router

This example uses [React Router](https://reactrouter.com/en/main) to setup routes for each tab and synchronize the selection with the URL.

```tsx
import {Route, Routes, useLocation} from "react-router-dom";
import {Tabs, Tab} from "@heroui/react";

function AppTabs() {
  const {pathname} = useLocation();

  return (
    <div className="flex flex-col gap-2">
      <Tabs selectedKey={pathname} aria-label="Tabs">
        <Tab key="/" href="/" title="Home" />
        <Tab key="/photos" href="/photos" title="Photos" />
        <Tab key="/music" href="/music" title="Music" />
        <Tab key="/videos" href="/videos" title="Videos" />
      </Tabs>
      <Routes>
        <Route path="/" element={<HomePage />} />
        <Route path="/photos" element={<PhotosPage />} />
        <Route path="/music" element={<MusicPage />} />
        <Route path="/videos" element={<VideosPage />} />
      </Routes>
    </div>
  );
}
```

> **Note**: See the [Routing guide](/docs/guide/routing) to learn how to set up the router for your framework.

### With Form

<CodeDemo title="With Form" files={tabsContent.form} />

## Slots

- **base**: The main tabs slot, it wraps the items and the panels.
- **tabList**: The tab list slot, it wraps the tab items.
- **tab**: The tab slot, it wraps the tab item.
- **tabContent**: The tab content slot, it wraps the tab content.
- **cursor**: The cursor slot, it wraps the cursor. This is only visible when `disableAnimation=false`
- **panel**: The panel slot, it wraps the tab panel (content).
- **tabWrapper**: The tab wrapper slot, it wraps the tab and the tab content.

### Custom Styles

You can customize the `Tabs` component by passing custom Tailwind CSS classes to the component slots.

<CodeDemo title="Custom Styles" files={tabsContent.customStyles} />

<Spacer y={4} />

## Data Attributes

`Tab` has the following attributes on the `base` element:

- **data-selected**:
  When the tab is selected.
- **data-disabled**:
  When the tab is disabled.
- **data-hover**:
  When the tab is being hovered. Based on [useHover](https://react-spectrum.adobe.com/react-aria/useHover.html).
- **data-hover-selected**:
  When the tab is being hovered and is not selected. Based on [useHover](https://react-spectrum.adobe.com/react-aria/useHover.html) and `selected` state.
- **data-focus**:
  When the tab is being focused. Based on [useFocusRing](https://react-spectrum.adobe.com/react-aria/useFocusRing.html).
- **data-focus-visible**:
  When the tab is being focused with the keyboard. Based on [useFocusRing](https://react-spectrum.adobe.com/react-aria/useFocusRing.html).
- **data-pressed**:
  When the tab is pressed. Based on [usePress](https://react-spectrum.adobe.com/react-aria/usePress.html).

<Spacer y={4} />

## Accessibility

- Support for mouse, touch, and keyboard interactions on tabs.
- Keyboard event support for arrows keys.
- Support for disabled tabs.
- Follows the tabs ARIA pattern, semantically linking tabs and their associated tab panels.
- Focus management for tab panels without any focusable children.

<Spacer y={4} />

## API

### Tabs Props

<APITable
  data={[
    {
      attribute: "children*",
      type: "ReactNode | ((item: T) => ReactElement)",
      description: "The list of tabs.",
      default: "-"
    },
    {
      attribute: "variant",
      type: "solid | bordered | light | underlined",
      description: "The tabs appearance style.",
      default: "solid"
    },
    {
      attribute: "color",
      type: "default | primary | secondary | success | warning | danger",
      description: "The tabs color theme.",
      default: "default"
    },
    {
      attribute: "size",
      type: "sm | md | lg",
      description: "The tabs size.",
      default: "md"
    },
    {
      attribute: "radius",
      type: "none | sm | md | lg | full",
      description: "The tabs border radius.",
      default: "-"
    },
    {
      attribute: "fullWidth",
      type: "boolean",
      description: "Whether the tabs should take the full width of its parent.",
      default: "false"
    },
    {
      attribute: "items",
      type: "Iterable<T>",
      description: "The list of tabs (dynamic).",
      default: "-"
    },
    {
      attribute: "disabledKeys",
      type: "React.Key[]",
      description: "The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with.",
      default: "-"
    },
    {
      attribute: "selectedKey",
      type: "React.Key",
      description: "The key for the currently selected item.",
      default: "-"
    },
    {
      attribute: "defaultSelectedKey",
      type: "React.Key",
      description: "The key for the initially selected item.",
      default: "-"
    },
    {
      attribute: "shouldSelectOnPressUp",
      type: "boolean",
      description: "Whether the tabs selection should occur on press up instead of press down.",
      default: "true"
    },
    {
      attribute: "keyboardActivation",
      type: "automatic | manual",
      description: "Whether tabs are activated automatically on focus or manually.",
      default: "automatic"
    },
    {
      attribute: "disableCursorAnimation",
      type: "boolean",
      description: "Whether the cursor should be hidden.",
      default: "false"
    },
    {
      attribute: "isDisabled",
      type: "boolean",
      description: "Whether the tab list should be disabled.",
      default: "false"
    },
    {
      attribute: "disableAnimation",
      type: "boolean",
      description: "Whether the tab list should be animated.",
      default: "false"
    },
    {
      attribute: "classNames",
      type: "Partial<Record<\"base\"｜ \"tabList\"｜ \"tab\"｜ \"tabContent\"｜ \"cursor\" ｜ \"panel\" ｜ \"tabWrapper\", string>>",
      description: "Allows to set custom class names for the card slots.",
      default: "-"
    },
    {
      attribute: "placement",
      type: "top | bottom | start | end",
      description: "The position of the tabs.",
      default: "top"
    },
    {
      attribute: "isVertical",
      type: "boolean",
      description: "Whether the tabs are vertical.",
      default: "false"
    },
    {
      attribute: "destroyInactiveTabPanel",
      type: "boolean",
      description: "Whether to destroy inactive tab panel when switching tabs. Inactive tab panels are inert and cannot be interacted with.",
      default: "true"
    }
  ]}
/>

### Tabs Events

<APITable
  data={[
    {
      attribute: "onSelectionChange",
      type: "(key: React.Key) => any",
      description: "Handler that is called when the selection changes.",
      default: "-"
    }
  ]}
/>

### Tab Props

<APITable
  data={[
    {
      attribute: "tabRef",
      type: "RefObject<HTMLButtonElement>",
      description: "A ref to the tab item.",
      default: "-"
    },
    {
      attribute: "children*",
      type: "ReactNode",
      description: "The content of the tab.",
      default: "-"
    },
    {
      attribute: "title",
      type: "ReactNode",
      description: "The title of the tab.",
      default: "-"
    },
    {
      attribute: "titleValue",
      type: "string",
      description: "A string representation of the item's contents. Use this when the title is not readable.",
      default: "-"
    },
    {
      attribute: "href",
      type: "string",
      description: "A URL to link to. See MDN.",
      default: "-"
    },
    {
      attribute: "target",
      type: "HTMLAttributeAnchorTarget",
      description: "The target window for the link. See MDN.",
      default: "-"
    },
    {
      attribute: "rel",
      type: "string",
      description: "The relationship between the linked resource and the current page. See MDN.",
      default: "-"
    },
    {
      attribute: "download",
      type: "boolean | string",
      description: "Causes the browser to download the linked URL. A string may be provided to suggest a file name. See MDN.",
      default: "-"
    },
    {
      attribute: "ping",
      type: "string",
      description: "A space-separated list of URLs to ping when the link is followed. See MDN.",
      default: "-"
    },
    {
      attribute: "referrerPolicy",
      type: "HTMLAttributeReferrerPolicy",
      description: "How much of the referrer to send when following the link. See MDN.",
      default: "-"
    },
    {
      attribute: "shouldSelectOnPressUp",
      type: "boolean",
      description: "Whether the tab selection should occur on press up instead of press down.",
      default: "-"
    }
  ]}
/>
